{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "794696e4",
   "metadata": {},
   "source": [
    "定义合适的rui是很重要的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "03bd465c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import math\n",
    "import random \n",
    "import time"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d5d67cd",
   "metadata": {},
   "source": [
    "数据预处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cdff2203",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数据集中的电影部数：7142\n",
      "数据集中的用户数：597\n"
     ]
    }
   ],
   "source": [
    "df=pd.read_csv('E:/推荐系统/数据集/MovieLens/ml-latest/ratings.csv')\n",
    "df_copy1=df.copy()     #正负反馈皆有\n",
    "df_copy2=df.copy()      \n",
    "df_copy2=df_copy1[df_copy1['rating']>2.9]     #只有正反馈\n",
    "df=df_copy2.iloc[:50000].sample(frac=1,random_state=1)[['userId','movieId']]\n",
    "print(\"数据集中的电影部数：%d\"%len(df['movieId'].unique()))\n",
    "print(\"数据集中的用户数：%d\"%len(df['userId'].unique()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "04ef6818",
   "metadata": {},
   "outputs": [],
   "source": [
    "#按比例划分训练集和测试集\n",
    "proportion=0.8\n",
    "train=df.iloc[0:int(proportion*df.shape[0])]\n",
    "test=df.iloc[int(proportion*df.shape[0]):]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbac3903",
   "metadata": {},
   "source": [
    "定义需要函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f4945699",
   "metadata": {},
   "outputs": [],
   "source": [
    "#物品流行度计算函数：\n",
    "def items_popularity(train):\n",
    "    train_copy=train.copy()\n",
    "    train_copy['popularity']=1\n",
    "    df_movie_popularity=train_copy.groupby('movieId').agg('sum').reset_index()\n",
    "    df_movie_popularity=df_movie_popularity[['movieId','popularity']]\n",
    "    df_movie_popularity\n",
    "    return df_movie_popularity\n",
    "#计算物品流行度，主要为了后面计算推荐名单结果的流行度\n",
    "df_movie_popularity=items_popularity(train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9a928c76",
   "metadata": {},
   "outputs": [],
   "source": [
    "#评价指标\n",
    "def Recall_func(item1,item2):\n",
    "    return len( list(set(item1).intersection(set(item2))) )/len(item2)\n",
    "def Precision_func(item1,item2):\n",
    "    return len( list(set(item1).intersection(set(item2))) )/len(item1)\n",
    "def Coverage_func(item1,item2):\n",
    "    return len(item1)/len(train['movieId'].unique())\n",
    "def Popularity_func(item1,df_movie_popularity):\n",
    "    popularity=0\n",
    "    n=0\n",
    "    for item in item1:\n",
    "        popularity=popularity+df_movie_popularity[df_movie_popularity['movieId']==item]['popularity'].values\n",
    "        n+=1\n",
    "    return popularity/n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "44c7a2fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "#初始化P,Q向量\n",
    "def InitializePQ(F,all_users_set,all_items_set):\n",
    "    P={user:np.random.randn(F) for user in all_users_set}\n",
    "    Q={item:np.random.randn(F) for item in all_items_set}\n",
    "    return P,Q\n",
    "\n",
    "def sigmoid(inx):\n",
    "    if inx >= 0:  # 对sigmoid函数的优化，避免了出现极大的数据溢出\n",
    "        return 1.0 / (1 + np.exp(-inx))\n",
    "    else:\n",
    "        return np.exp(inx) / (1 + np.exp(inx))\n",
    "\n",
    "def logistic_function(x):\n",
    "    return .5 * (1 + np.tanh(.5 * x))\n",
    "\n",
    "#得到rui的预测值\n",
    "def Predict(P_user,Q_item):\n",
    "    return sigmoid( np.multiply( P_user,Q_item ).sum() )\n",
    "    #return np.multiply( P_user,Q_item ).sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "54cd2966",
   "metadata": {},
   "outputs": [],
   "source": [
    "#针对一个用户训练集的交互物品，从其未交互过的物品中按照样本出现的频率（热门程度）生成负样本\n",
    "def RandSelectNegativeSamples(items_set,all_items,ratio):\n",
    "    '''\n",
    "    items_set:目标用户训练集交互过的物品集合\n",
    "    all_items:训练集所有物品列表\n",
    "    ratio:负样本与正样本比例\n",
    "    N_negative_samples：已经生成的负样本数\n",
    "    ret：存储样本和标签的字典\n",
    "    '''\n",
    "    ret={}\n",
    "    for item in items_set:\n",
    "        ret[item]=1\n",
    "    N_negative_samples=0\n",
    "    \n",
    "    if ratio*len(items_set)>len(set(all_items))-len(items_set):      #有些用户的正样本数目太多，导致训练集中剩余未交互的物品无法满足负样本数木\n",
    "        for item in all_items:\n",
    "            if item not in ret:\n",
    "                ret[item]=0\n",
    "    else:\n",
    "        while N_negative_samples<ratio*len(items_set):\n",
    "            item=all_items[random.randint(0,len(all_items)-1)]\n",
    "            if item in ret:\n",
    "                continue\n",
    "            ret[item]=0\n",
    "            N_negative_samples+=1\n",
    "        \n",
    "    return ret\n",
    "\n",
    "#梯度下降法计算P，Q\n",
    "def LatentFactorModel(train,F,alpha,lambda_,ratio,steps):\n",
    "    '''\n",
    "    F:隐特征个数\n",
    "    alpha：学习率\n",
    "    lambda_：正则项系数\n",
    "    ratio：负样本与正样本比例\n",
    "    steps：迭代总次数\n",
    "    all_users_set：训练集所有用户集合\n",
    "    all_items_set：训练集所有物品集合\n",
    "    all_items：训练集所有物品列表，包括重复交互\n",
    "    '''\n",
    "    all_users_set=set(train['userId'])\n",
    "    all_items_set=set(train['movieId'])\n",
    "    P,Q=InitializePQ(F,all_users_set,all_items_set)\n",
    "    all_items=list(train['movieId'])\n",
    "    \n",
    "    for step in range(0,steps):\n",
    "        err_sum=0\n",
    "        for user, result in train.groupby('userId'):\n",
    "            \n",
    "            result=result.reset_index(drop=True)\n",
    "            items_set=set(result['movieId'])\n",
    "            samples=RandSelectNegativeSamples(items_set,all_items,ratio)  #针对该用户，生成完整的正负样本，用来训练P，Q\n",
    "            for item,rui in samples.items():                              #随机梯度下降方法，每次拿出一个样本和样本标签来更新P和Q中对应的参数\n",
    "                eui= rui - Predict(P[user],Q[item])\n",
    "                err_sum+=eui**2\n",
    "                P[user]+=alpha*( eui*Q[item]-lambda_*P[user] )            #向量化加速\n",
    "                Q[item]+=alpha*( eui*P[user]-lambda_*Q[item] )\n",
    "\n",
    "        print(\"第%d步损失为：%f\"%(step+1,err_sum))\n",
    "        alpha*=0.9                                                        #学习率衰减\n",
    "    return P,Q\n",
    "\n",
    "#计算用户对除已经交互过的物品外的所有物品的感兴趣程度，排序，返回前K个最感兴趣的\n",
    "def Recommand(user,P,Q,train_items_set,K):\n",
    "    rank={}\n",
    "    \n",
    "    #计算感兴趣程度\n",
    "    for item in Q.keys():\n",
    "        if item in train_items_set:\n",
    "            continue\n",
    "        if item not in rank:\n",
    "            rank[item]=(np.multiply(P[user],Q[item])).sum()\n",
    "            \n",
    "    #返回前K个最感兴趣的\n",
    "    index=list(np.argsort(list(rank.values()))[-K:])\n",
    "    K_item1=[]\n",
    "    keys_lst=list(rank.keys())\n",
    "    for i in range(len(index)):\n",
    "        K_item1.append(keys_lst[index[i]])\n",
    "        \n",
    "    return K_item1    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "735cf18d",
   "metadata": {},
   "source": [
    "训练PQ，预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "2bbe9a61",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第1步损失为：23876.937854\n",
      "第2步损失为：8590.399924\n",
      "第3步损失为：9908.291136\n",
      "第4步损失为：11449.292637\n",
      "第5步损失为：13918.265579\n",
      "第6步损失为：17537.279328\n",
      "第7步损失为：17085.083397\n",
      "第8步损失为：18604.284927\n",
      "第9步损失为：18109.926815\n",
      "第10步损失为：18589.027322\n",
      "第11步损失为：21200.099938\n",
      "第12步损失为：20083.903697\n",
      "第13步损失为：20081.603014\n",
      "第14步损失为：19791.573930\n",
      "第15步损失为：18747.861725\n",
      "第16步损失为：18431.373707\n",
      "第17步损失为：17998.029804\n",
      "第18步损失为：17589.620061\n",
      "第19步损失为：15801.626490\n",
      "第20步损失为：14710.914431\n",
      "第21步损失为：14018.724346\n",
      "第22步损失为：13103.818639\n",
      "第23步损失为：12765.023624\n",
      "第24步损失为：12392.753513\n",
      "第25步损失为：11813.504632\n",
      "第26步损失为：11395.848508\n",
      "第27步损失为：11669.044808\n",
      "第28步损失为：11351.536267\n",
      "第29步损失为：10894.410841\n",
      "第30步损失为：10568.875032\n",
      "第31步损失为：10450.003825\n",
      "第32步损失为：10372.850530\n",
      "第33步损失为：10388.018698\n",
      "第34步损失为：10319.732428\n",
      "第35步损失为：10280.889769\n",
      "第36步损失为：10371.791016\n",
      "第37步损失为：10476.264492\n",
      "第38步损失为：10450.779293\n",
      "第39步损失为：10489.116999\n",
      "第40步损失为：10582.475075\n",
      "第41步损失为：10570.283020\n",
      "第42步损失为：10722.614509\n",
      "第43步损失为：10947.672763\n",
      "第44步损失为：11119.486205\n",
      "第45步损失为：11117.094591\n",
      "第46步损失为：11329.483543\n",
      "第47步损失为：11599.777127\n",
      "第48步损失为：11846.099596\n",
      "第49步损失为：11973.450399\n",
      "第50步损失为：12060.244512\n",
      "第51步损失为：12260.126750\n",
      "第52步损失为：12415.760074\n",
      "第53步损失为：12639.075182\n",
      "第54步损失为：13030.412780\n",
      "第55步损失为：13343.870352\n",
      "第56步损失为：13446.313106\n",
      "第57步损失为：13792.617231\n",
      "第58步损失为：14161.103734\n",
      "第59步损失为：14389.631999\n",
      "第60步损失为：14577.535126\n",
      "第61步损失为：14853.761596\n",
      "第62步损失为：15008.279740\n",
      "第63步损失为：15270.536838\n",
      "第64步损失为：15582.594672\n",
      "第65步损失为：15918.170701\n",
      "第66步损失为：16411.125452\n",
      "第67步损失为：16520.882079\n",
      "第68步损失为：16865.235570\n",
      "第69步损失为：17111.568123\n",
      "第70步损失为：17439.772816\n",
      "第71步损失为：17732.449285\n",
      "第72步损失为：18119.903968\n",
      "第73步损失为：18202.669857\n",
      "第74步损失为：18500.151898\n",
      "第75步损失为：19017.208776\n",
      "第76步损失为：19233.792565\n",
      "第77步损失为：19551.284556\n",
      "第78步损失为：19741.967054\n",
      "第79步损失为：20163.280414\n",
      "第80步损失为：20316.710465\n",
      "第81步损失为：20812.010056\n",
      "第82步损失为：21069.293016\n",
      "第83步损失为：21424.031927\n",
      "第84步损失为：21798.288054\n",
      "第85步损失为：22044.755790\n",
      "第86步损失为：22256.621211\n",
      "第87步损失为：22640.223878\n",
      "第88步损失为：22896.461320\n",
      "第89步损失为：23109.953638\n",
      "第90步损失为：23304.749615\n",
      "第91步损失为：23779.367273\n",
      "第92步损失为：24124.046854\n",
      "第93步损失为：24220.873397\n",
      "第94步损失为：24686.576504\n",
      "第95步损失为：24936.234102\n",
      "第96步损失为：25294.611615\n",
      "第97步损失为：25510.556875\n",
      "第98步损失为：25816.421856\n",
      "第99步损失为：26225.399194\n",
      "第100步损失为：26641.905642\n",
      "第101步损失为：26849.325942\n",
      "第102步损失为：27232.482129\n",
      "第103步损失为：27535.198129\n",
      "第104步损失为：27804.909009\n",
      "第105步损失为：28146.853964\n",
      "第106步损失为：28396.775840\n",
      "第107步损失为：28630.482678\n",
      "第108步损失为：29026.087541\n",
      "第109步损失为：29298.327393\n",
      "第110步损失为：29570.500923\n",
      "第111步损失为：29823.931329\n",
      "第112步损失为：30080.207186\n",
      "第113步损失为：30372.488023\n",
      "第114步损失为：30653.927994\n",
      "第115步损失为：30914.558891\n",
      "第116步损失为：31114.356148\n",
      "第117步损失为：31404.580403\n",
      "第118步损失为：31564.790646\n",
      "第119步损失为：31673.819514\n",
      "第120步损失为：32274.197869\n",
      "用时:724.102007秒\n"
     ]
    }
   ],
   "source": [
    "start=time.time()\n",
    "P,Q=LatentFactorModel(train,20,0.2,0.01,4,120)\n",
    "end=time.time()\n",
    "print(\"用时:%f秒\"%(end-start))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "2bf6eec5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "已预测1个用户\n",
      "已预测2个用户\n",
      "已预测3个用户\n",
      "已预测4个用户\n",
      "已预测5个用户\n",
      "已预测6个用户\n",
      "已预测7个用户\n",
      "已预测8个用户\n",
      "已预测9个用户\n",
      "已预测10个用户\n",
      "已预测11个用户\n",
      "已预测12个用户\n",
      "已预测13个用户\n",
      "已预测14个用户\n",
      "已预测15个用户\n",
      "已预测16个用户\n",
      "已预测17个用户\n",
      "已预测18个用户\n",
      "已预测19个用户\n",
      "已预测20个用户\n",
      "已预测21个用户\n",
      "已预测22个用户\n",
      "已预测23个用户\n",
      "已预测24个用户\n",
      "已预测25个用户\n",
      "已预测26个用户\n",
      "已预测27个用户\n",
      "已预测28个用户\n",
      "已预测29个用户\n",
      "已预测30个用户\n",
      "已预测31个用户\n",
      "已预测32个用户\n",
      "已预测33个用户\n",
      "已预测34个用户\n",
      "已预测35个用户\n",
      "已预测36个用户\n",
      "已预测37个用户\n",
      "已预测38个用户\n",
      "已预测39个用户\n",
      "已预测40个用户\n",
      "已预测41个用户\n",
      "已预测42个用户\n",
      "已预测43个用户\n",
      "已预测44个用户\n",
      "已预测45个用户\n",
      "已预测46个用户\n",
      "已预测47个用户\n",
      "已预测48个用户\n",
      "已预测49个用户\n",
      "已预测50个用户\n",
      "已预测51个用户\n",
      "已预测52个用户\n",
      "已预测53个用户\n",
      "已预测54个用户\n",
      "已预测55个用户\n",
      "已预测56个用户\n",
      "已预测57个用户\n",
      "已预测58个用户\n",
      "已预测59个用户\n",
      "已预测60个用户\n",
      "已预测61个用户\n",
      "已预测62个用户\n",
      "已预测63个用户\n",
      "已预测64个用户\n",
      "已预测65个用户\n",
      "已预测66个用户\n",
      "已预测67个用户\n",
      "已预测68个用户\n",
      "已预测69个用户\n",
      "已预测70个用户\n",
      "已预测71个用户\n",
      "已预测72个用户\n",
      "已预测73个用户\n",
      "已预测74个用户\n",
      "已预测75个用户\n",
      "已预测76个用户\n",
      "已预测77个用户\n",
      "已预测78个用户\n",
      "已预测79个用户\n",
      "已预测80个用户\n",
      "已预测81个用户\n",
      "已预测82个用户\n",
      "已预测83个用户\n",
      "已预测84个用户\n",
      "已预测85个用户\n",
      "已预测86个用户\n",
      "已预测87个用户\n",
      "已预测88个用户\n",
      "已预测89个用户\n",
      "已预测90个用户\n",
      "已预测91个用户\n",
      "已预测92个用户\n",
      "已预测93个用户\n",
      "已预测94个用户\n",
      "已预测95个用户\n",
      "已预测96个用户\n",
      "已预测97个用户\n",
      "已预测98个用户\n",
      "已预测99个用户\n",
      "已预测100个用户\n",
      "已预测101个用户\n",
      "已预测102个用户\n",
      "已预测103个用户\n",
      "已预测104个用户\n",
      "已预测105个用户\n",
      "已预测106个用户\n",
      "已预测107个用户\n",
      "已预测108个用户\n",
      "已预测109个用户\n",
      "已预测110个用户\n",
      "已预测111个用户\n",
      "已预测112个用户\n",
      "已预测113个用户\n",
      "已预测114个用户\n",
      "已预测115个用户\n",
      "已预测116个用户\n",
      "已预测117个用户\n",
      "已预测118个用户\n",
      "已预测119个用户\n",
      "已预测120个用户\n",
      "已预测121个用户\n",
      "已预测122个用户\n",
      "已预测123个用户\n",
      "已预测124个用户\n",
      "已预测125个用户\n",
      "已预测126个用户\n",
      "已预测127个用户\n",
      "已预测128个用户\n",
      "已预测129个用户\n",
      "已预测130个用户\n",
      "已预测131个用户\n",
      "已预测132个用户\n",
      "已预测133个用户\n",
      "已预测134个用户\n",
      "已预测135个用户\n",
      "已预测136个用户\n",
      "已预测137个用户\n",
      "已预测138个用户\n",
      "已预测139个用户\n",
      "已预测140个用户\n",
      "已预测141个用户\n",
      "已预测142个用户\n",
      "已预测143个用户\n",
      "已预测144个用户\n",
      "已预测145个用户\n",
      "已预测146个用户\n",
      "已预测147个用户\n",
      "已预测148个用户\n",
      "已预测149个用户\n",
      "已预测150个用户\n",
      "已预测151个用户\n",
      "已预测152个用户\n",
      "已预测153个用户\n",
      "已预测154个用户\n",
      "已预测155个用户\n",
      "已预测156个用户\n",
      "已预测157个用户\n",
      "已预测158个用户\n",
      "已预测159个用户\n",
      "已预测160个用户\n",
      "已预测161个用户\n",
      "已预测162个用户\n",
      "已预测163个用户\n",
      "已预测164个用户\n",
      "已预测165个用户\n",
      "已预测166个用户\n",
      "已预测167个用户\n",
      "已预测168个用户\n",
      "已预测169个用户\n",
      "已预测170个用户\n",
      "已预测171个用户\n",
      "已预测172个用户\n",
      "已预测173个用户\n",
      "已预测174个用户\n",
      "已预测175个用户\n",
      "已预测176个用户\n",
      "已预测177个用户\n",
      "已预测178个用户\n",
      "已预测179个用户\n",
      "已预测180个用户\n",
      "已预测181个用户\n",
      "已预测182个用户\n",
      "已预测183个用户\n",
      "无法对用户198做出预测，因为训练集中没有该用户行为\n",
      "已预测184个用户\n",
      "已预测185个用户\n",
      "已预测186个用户\n",
      "已预测187个用户\n",
      "已预测188个用户\n",
      "已预测189个用户\n",
      "已预测190个用户\n",
      "已预测191个用户\n",
      "已预测192个用户\n",
      "已预测193个用户\n",
      "已预测194个用户\n",
      "已预测195个用户\n",
      "已预测196个用户\n",
      "已预测197个用户\n",
      "已预测198个用户\n",
      "已预测199个用户\n",
      "已预测200个用户\n",
      "已预测201个用户\n",
      "已预测202个用户\n",
      "已预测203个用户\n",
      "已预测204个用户\n",
      "已预测205个用户\n",
      "已预测206个用户\n",
      "已预测207个用户\n",
      "已预测208个用户\n",
      "已预测209个用户\n",
      "已预测210个用户\n",
      "已预测211个用户\n",
      "已预测212个用户\n",
      "已预测213个用户\n",
      "已预测214个用户\n",
      "已预测215个用户\n",
      "已预测216个用户\n",
      "已预测217个用户\n",
      "已预测218个用户\n",
      "已预测219个用户\n",
      "已预测220个用户\n",
      "已预测221个用户\n",
      "已预测222个用户\n",
      "已预测223个用户\n",
      "已预测224个用户\n",
      "已预测225个用户\n",
      "已预测226个用户\n",
      "已预测227个用户\n",
      "已预测228个用户\n",
      "已预测229个用户\n",
      "已预测230个用户\n",
      "已预测231个用户\n",
      "已预测232个用户\n",
      "已预测233个用户\n",
      "已预测234个用户\n",
      "已预测235个用户\n",
      "已预测236个用户\n",
      "已预测237个用户\n",
      "已预测238个用户\n",
      "已预测239个用户\n",
      "已预测240个用户\n",
      "已预测241个用户\n",
      "已预测242个用户\n",
      "已预测243个用户\n",
      "已预测244个用户\n",
      "已预测245个用户\n",
      "已预测246个用户\n",
      "已预测247个用户\n",
      "已预测248个用户\n",
      "已预测249个用户\n",
      "已预测250个用户\n",
      "已预测251个用户\n",
      "已预测252个用户\n",
      "已预测253个用户\n",
      "无法对用户281做出预测，因为训练集中没有该用户行为\n",
      "已预测254个用户\n",
      "已预测255个用户\n",
      "已预测256个用户\n",
      "已预测257个用户\n",
      "已预测258个用户\n",
      "已预测259个用户\n",
      "已预测260个用户\n",
      "已预测261个用户\n",
      "已预测262个用户\n",
      "已预测263个用户\n",
      "已预测264个用户\n",
      "已预测265个用户\n",
      "已预测266个用户\n",
      "已预测267个用户\n",
      "已预测268个用户\n",
      "已预测269个用户\n",
      "已预测270个用户\n",
      "已预测271个用户\n",
      "已预测272个用户\n",
      "已预测273个用户\n",
      "已预测274个用户\n",
      "已预测275个用户\n",
      "已预测276个用户\n",
      "已预测277个用户\n",
      "已预测278个用户\n",
      "已预测279个用户\n",
      "已预测280个用户\n",
      "已预测281个用户\n",
      "已预测282个用户\n",
      "已预测283个用户\n",
      "已预测284个用户\n",
      "已预测285个用户\n",
      "已预测286个用户\n",
      "已预测287个用户\n",
      "已预测288个用户\n",
      "已预测289个用户\n",
      "已预测290个用户\n",
      "已预测291个用户\n",
      "已预测292个用户\n",
      "已预测293个用户\n",
      "已预测294个用户\n",
      "已预测295个用户\n",
      "已预测296个用户\n",
      "已预测297个用户\n",
      "已预测298个用户\n",
      "已预测299个用户\n",
      "已预测300个用户\n",
      "已预测301个用户\n",
      "已预测302个用户\n",
      "已预测303个用户\n",
      "已预测304个用户\n",
      "已预测305个用户\n",
      "已预测306个用户\n",
      "已预测307个用户\n",
      "已预测308个用户\n",
      "已预测309个用户\n",
      "已预测310个用户\n",
      "已预测311个用户\n",
      "已预测312个用户\n",
      "已预测313个用户\n",
      "已预测314个用户\n",
      "已预测315个用户\n",
      "已预测316个用户\n",
      "已预测317个用户\n",
      "已预测318个用户\n",
      "已预测319个用户\n",
      "已预测320个用户\n",
      "已预测321个用户\n",
      "已预测322个用户\n",
      "已预测323个用户\n",
      "已预测324个用户\n",
      "已预测325个用户\n",
      "已预测326个用户\n",
      "已预测327个用户\n",
      "已预测328个用户\n",
      "已预测329个用户\n",
      "已预测330个用户\n",
      "已预测331个用户\n",
      "已预测332个用户\n",
      "已预测333个用户\n",
      "已预测334个用户\n",
      "已预测335个用户\n",
      "已预测336个用户\n",
      "已预测337个用户\n",
      "已预测338个用户\n",
      "已预测339个用户\n",
      "已预测340个用户\n",
      "已预测341个用户\n",
      "已预测342个用户\n",
      "已预测343个用户\n",
      "已预测344个用户\n",
      "已预测345个用户\n",
      "已预测346个用户\n",
      "已预测347个用户\n",
      "已预测348个用户\n",
      "已预测349个用户\n",
      "已预测350个用户\n",
      "已预测351个用户\n",
      "已预测352个用户\n",
      "已预测353个用户\n",
      "已预测354个用户\n",
      "已预测355个用户\n",
      "已预测356个用户\n",
      "已预测357个用户\n",
      "已预测358个用户\n",
      "已预测359个用户\n",
      "已预测360个用户\n",
      "已预测361个用户\n",
      "已预测362个用户\n",
      "已预测363个用户\n",
      "已预测364个用户\n",
      "已预测365个用户\n",
      "已预测366个用户\n",
      "已预测367个用户\n",
      "已预测368个用户\n",
      "已预测369个用户\n",
      "已预测370个用户\n",
      "已预测371个用户\n",
      "已预测372个用户\n",
      "已预测373个用户\n",
      "已预测374个用户\n",
      "已预测375个用户\n",
      "已预测376个用户\n",
      "已预测377个用户\n",
      "已预测378个用户\n",
      "已预测379个用户\n",
      "已预测380个用户\n",
      "已预测381个用户\n",
      "已预测382个用户\n",
      "已预测383个用户\n",
      "已预测384个用户\n",
      "已预测385个用户\n",
      "已预测386个用户\n",
      "已预测387个用户\n",
      "已预测388个用户\n",
      "已预测389个用户\n",
      "已预测390个用户\n",
      "已预测391个用户\n",
      "已预测392个用户\n",
      "已预测393个用户\n",
      "已预测394个用户\n",
      "已预测395个用户\n",
      "已预测396个用户\n",
      "已预测397个用户\n",
      "已预测398个用户\n",
      "已预测399个用户\n",
      "已预测400个用户\n",
      "已预测401个用户\n",
      "已预测402个用户\n",
      "已预测403个用户\n",
      "已预测404个用户\n",
      "已预测405个用户\n",
      "已预测406个用户\n",
      "已预测407个用户\n",
      "已预测408个用户\n",
      "已预测409个用户\n",
      "已预测410个用户\n",
      "已预测411个用户\n",
      "已预测412个用户\n",
      "已预测413个用户\n",
      "已预测414个用户\n",
      "已预测415个用户\n",
      "已预测416个用户\n",
      "已预测417个用户\n",
      "已预测418个用户\n",
      "已预测419个用户\n",
      "已预测420个用户\n",
      "已预测421个用户\n",
      "已预测422个用户\n",
      "已预测423个用户\n",
      "已预测424个用户\n",
      "已预测425个用户\n",
      "已预测426个用户\n",
      "已预测427个用户\n",
      "已预测428个用户\n",
      "已预测429个用户\n",
      "已预测430个用户\n",
      "已预测431个用户\n",
      "已预测432个用户\n",
      "已预测433个用户\n",
      "已预测434个用户\n",
      "已预测435个用户\n",
      "已预测436个用户\n",
      "已预测437个用户\n",
      "已预测438个用户\n",
      "已预测439个用户\n",
      "已预测440个用户\n",
      "已预测441个用户\n",
      "已预测442个用户\n",
      "已预测443个用户\n",
      "已预测444个用户\n",
      "已预测445个用户\n",
      "已预测446个用户\n",
      "已预测447个用户\n",
      "已预测448个用户\n",
      "已预测449个用户\n",
      "已预测450个用户\n",
      "已预测451个用户\n",
      "已预测452个用户\n",
      "已预测453个用户\n",
      "已预测454个用户\n",
      "已预测455个用户\n",
      "已预测456个用户\n",
      "已预测457个用户\n",
      "已预测458个用户\n",
      "已预测459个用户\n",
      "已预测460个用户\n",
      "已预测461个用户\n",
      "已预测462个用户\n",
      "已预测463个用户\n",
      "已预测464个用户\n",
      "已预测465个用户\n",
      "已预测466个用户\n",
      "已预测467个用户\n",
      "已预测468个用户\n",
      "已预测469个用户\n",
      "已预测470个用户\n",
      "已预测471个用户\n",
      "已预测472个用户\n",
      "已预测473个用户\n",
      "已预测474个用户\n",
      "已预测475个用户\n",
      "已预测476个用户\n",
      "已预测477个用户\n",
      "已预测478个用户\n",
      "已预测479个用户\n",
      "已预测480个用户\n",
      "已预测481个用户\n",
      "已预测482个用户\n",
      "已预测483个用户\n",
      "已预测484个用户\n",
      "已预测485个用户\n",
      "已预测486个用户\n",
      "已预测487个用户\n",
      "已预测488个用户\n",
      "已预测489个用户\n",
      "已预测490个用户\n",
      "已预测491个用户\n",
      "已预测492个用户\n",
      "已预测493个用户\n",
      "已预测494个用户\n",
      "已预测495个用户\n",
      "已预测496个用户\n",
      "已预测497个用户\n",
      "已预测498个用户\n",
      "已预测499个用户\n",
      "已预测500个用户\n",
      "已预测501个用户\n",
      "已预测502个用户\n",
      "已预测503个用户\n",
      "已预测504个用户\n",
      "已预测505个用户\n",
      "已预测506个用户\n",
      "已预测507个用户\n",
      "已预测508个用户\n",
      "已预测509个用户\n",
      "已预测510个用户\n",
      "已预测511个用户\n",
      "已预测512个用户\n",
      "已预测513个用户\n",
      "已预测514个用户\n",
      "已预测515个用户\n",
      "已预测516个用户\n",
      "已预测517个用户\n",
      "已预测518个用户\n",
      "已预测519个用户\n",
      "已预测520个用户\n",
      "已预测521个用户\n",
      "已预测522个用户\n",
      "已预测523个用户\n",
      "已预测524个用户\n",
      "已预测525个用户\n",
      "已预测526个用户\n",
      "已预测527个用户\n",
      "已预测528个用户\n",
      "已预测529个用户\n",
      "已预测530个用户\n",
      "已预测531个用户\n",
      "已预测532个用户\n",
      "已预测533个用户\n",
      "已预测534个用户\n",
      "已预测535个用户\n",
      "已预测536个用户\n",
      "已预测537个用户\n",
      "已预测538个用户\n",
      "已预测539个用户\n",
      "已预测540个用户\n",
      "已预测541个用户\n",
      "已预测542个用户\n",
      "已预测543个用户\n",
      "已预测544个用户\n",
      "已预测545个用户\n",
      "已预测546个用户\n",
      "最终覆盖率为0.180488\n",
      "最终召回率为0.068163\n",
      "最终准确率为0.033516\n",
      "平均流行度为19.367033\n",
      "____________\n",
      "运行时间为19.519928\n"
     ]
    }
   ],
   "source": [
    "start=time.time()\n",
    "'''\n",
    "Recall,Precision,Popularity：当前为止已预测出的结果的总平均，首字母小写表示具体每个用户的预测结果\n",
    "Item1,Item2：当前为止已经推荐的物品名单并集，测试集用户交互的物品集合并集。首字母小写代表单个用户的结果。\n",
    "Train_test_user：目标用户在训练集中的行为数据\n",
    "train_items_set：目标用户在训练集中的交互的物品名单\n",
    "'''\n",
    "Recall=0\n",
    "Precision=0\n",
    "Popularity=0\n",
    "N=1\n",
    "Item1=set()       #最终推荐物品名单\n",
    "Item2=set()       #测试集用户的行为名单\n",
    "K=20\n",
    "for test_user, result in test.groupby('userId'):\n",
    "    result=result.reset_index(drop=True)\n",
    "    \n",
    "    Train_test_user=train[train['userId']==test_user]\n",
    "    if Train_test_user.shape[0]==0:\n",
    "        print(\"无法对用户%d做出预测，因为训练集中没有该用户行为\"%test_user)\n",
    "        continue\n",
    "    train_items_set=set(Train_test_user['movieId'])\n",
    "    \n",
    "    item1=Recommand(test_user,P,Q,train_items_set,K)\n",
    "    item2=list(result['movieId'])\n",
    "    Item1=Item1.union(set(item1))\n",
    "    Item2=Item2.union(set(item2))\n",
    "    \n",
    "    popularity=Popularity_func(item1,df_movie_popularity)\n",
    "    recall=Recall_func(item1,item2)\n",
    "    precision=Precision_func(item1,item2)\n",
    "    Popularity=(N-1)*Popularity/N + popularity/N\n",
    "    Recall=(N-1)*Recall/N + recall/N\n",
    "    Precision=(N-1)*Precision/N + precision/N   \n",
    "    print(\"已预测%d个用户\"%N)\n",
    "    N+=1\n",
    "    \n",
    "    \n",
    "Coverage=Coverage_func(Item1,Item2)\n",
    "print(\"最终覆盖率为%f\"%Coverage)\n",
    "print(\"最终召回率为%f\"%Recall)  \n",
    "print(\"最终准确率为%f\"%Precision)\n",
    "print(\"平均流行度为%f\"%Popularity)\n",
    "end=time.time()\n",
    "print(\"____________\")\n",
    "print(\"运行时间为%f\"%(end-start))  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
